package cloudspace.controlpanel;

import java.io.IOException;
import java.util.List;
import java.util.Random;

import org.apache.log4j.Logger;
import org.springframework.security.core.GrantedAuthority;
import org.zkoss.util.media.Media;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.util.GenericComposer;
import org.zkoss.zul.Bandbox;
import org.zkoss.zul.Checkbox;
import org.zkoss.zul.Combobox;
import org.zkoss.zul.Comboitem;
import org.zkoss.zul.Label;
import org.zkoss.zul.Listbox;
import org.zkoss.zul.Listcell;
import org.zkoss.zul.Listhead;
import org.zkoss.zul.Listheader;
import org.zkoss.zul.Listitem;
import org.zkoss.zul.Tree;
import org.zkoss.zul.Window;

import cloudspace.controlpanel.filemanager.CSPathDirectoryTreeModel;
import cloudspace.controlpanel.filemanager.CSPathTreeitemRenderer;
import cloudspace.controlpanel.filemanager.FileManagerModel;
import cloudspace.security.GroupFactory;
import cloudspace.security.UserRegistry;
import cloudspace.security.permissions.PermissionsManager;
import cloudspace.vm.filesystem.CSPath;

import com.Ostermiller.util.CSVParser;


public class UploadMultipleUsersController extends GenericComposer
{
    // ~ Static/instance variables .............................................

    private Window uploadUsersWindow;

    private Checkbox hasHeaderRowCheckbox;

    private Listbox csvTable;

    // private Checkbox createUserDirectoriesCheckbox;
    private CSPath path;

    private Bandbox userDirectoryLocation;

    private Tree directoryTree;

    private Checkbox addUsersToGroupCheckbox;

    private Combobox groupToAddUsers;

    // private Button okButton;
    private Listbox authPolicy;

    private String[][] csvData;

    // private static final int AUTH_POLICY_LOCAL_PASSES_IN_FILE = 0;
    private static final int AUTH_POLICY_LOCAL_RANDOM_PASSES = 1;

    private static final int AUTH_POLICY_LDAP = 2;

    private static final Logger log = Logger.getLogger( UploadMultipleUsersController.class );

    private static final String PASSWORD_CHARS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

    private static final long serialVersionUID = 3668566701663835443L;


    // ~ Methods ...............................................................

    // ----------------------------------------------------------
    @Override
    @SuppressWarnings("unchecked")
    public void doAfterCompose( Component component ) throws Exception
    {
        super.doAfterCompose( component );
        path = null;
        uploadUsersWindow = (Window)component;

        hasHeaderRowCheckbox = (Checkbox)component.getFellow( "hasHeaderRowCheckbox" );
        csvTable = (Listbox)component.getFellow( "csvTable" );
        // createUserDirectoriesCheckbox = (Checkbox)component.getFellow(
        // "createUserDirectoriesCheckbox" );
        userDirectoryLocation = (Bandbox)component.getFellow( "userDirectoryLocation" );
        directoryTree = (Tree)component.getFellow( "directoryTree" );
        addUsersToGroupCheckbox = (Checkbox)component.getFellow( "addUsersToGroupCheckbox" );
        groupToAddUsers = (Combobox)component.getFellow( "groupToAddUsers" );
        // okButton = (Button) component.getFellow("okButton");
        authPolicy = (Listbox)component.getFellow( "authPolicy" );

        Media csvMedia = (Media)Executions.getCurrent()
            .getArg()
            .get( "csvMedia" );

        if ( csvMedia != null )
        {
            parseCsvMedia( csvMedia );
        }

        FileManagerModel fileModel = new FileManagerModel();
        directoryTree.setModel( new CSPathDirectoryTreeModel( fileModel ) );
        directoryTree.setTreeitemRenderer( new CSPathTreeitemRenderer() );

        List<GrantedAuthority> groups = PermissionsManager.getInstance()
            .getGroupList();

        for ( GrantedAuthority group : groups )
        {
            Comboitem item = new Comboitem();
            item.setValue( group );
            item.setLabel( group.getAuthority() );
            groupToAddUsers.getItems().add( item );
        }
    }


    // ----------------------------------------------------------
    public void parseCsvMedia( Media media )
    {
        csvData = null;

        try
        {
            csvData = CSVParser.parse( media.getReaderData() );
        }
        catch ( IOException e )
        {
            log.error( "There was an error parsing the CSV data.", e );
        }

        repopulateCsvTable();
    }


    // ----------------------------------------------------------
    public void onHasHeaderRowCheck( Event event )
    {
        repopulateCsvTable();
    }


    // ----------------------------------------------------------
    public void onDirectoryTreeSelect( Event event )
    {
        path = (CSPath)directoryTree.getSelectedItem().getValue();
        userDirectoryLocation.setValue( path.getQualifiedPath() );
        userDirectoryLocation.close();
    }


    // ----------------------------------------------------------
    public void onAddUsersToGroupCheck( Event event )
    {
        groupToAddUsers.setDisabled( !addUsersToGroupCheckbox.isChecked() );
    }


    // ----------------------------------------------------------
    @SuppressWarnings("unchecked")
    public void onOK( Event event )
    {
        if ( path == null )
        {
            uploadUsersWindow.getFellow( "messagePanel" ).setVisible( true );
            Label message = (Label)uploadUsersWindow.getFellow( "messageLabel" );
            message.setValue( "You must specify a workspace location" );
            return;
        }
        int usernameCol = -1, passwordCol = -1;

        Listhead head = csvTable.getListhead();
        List<Component> children = head.getChildren();

        for ( int i = 1; i < children.size(); i++ )
        {
            Listheader header = (Listheader)children.get( i );
            Listbox listbox = (Listbox)header.getChildren().get( 0 );

            Listitem selItem = listbox.getSelectedItem();

            if ( selItem != null )
            {
                if ( selItem.getLabel().equals( "Username" ) )
                {
                    usernameCol = i - 1;
                }
                else if ( selItem.getLabel().equals( "Password" ) )
                {
                    passwordCol = i - 1;
                }
            }
        }

        int authPol = authPolicy.getSelectedIndex();

        for ( Listitem row : (List<Listitem>)csvTable.getItems() )
        {
            String[] rowData = (String[])row.getValue();

            String username = ( usernameCol != -1 ) ? rowData[usernameCol]
                : null;

            if ( username == null )
            {
                continue;
            }

            if ( username.contains( "@" ) )
            {
                username = username.substring( 0, username.indexOf( '@' ) );
            }

            // TODO generate random passwords if necessary
            String authenticator;
            boolean needToMailPassword = false;

            if ( authPol == AUTH_POLICY_LDAP )
            {
                authenticator = "ldap";
            }
            else
            {
                authenticator = "local";

                if ( authPol == AUTH_POLICY_LOCAL_RANDOM_PASSES )
                {
                    needToMailPassword = true;
                }
            }

            String password = ( passwordCol != -1 ) ? rowData[passwordCol]
                : generateRandomPassword();

            UserRegistry.getInstance().createUser( username,
                password,
                path,
                authenticator );

            if ( needToMailPassword )
            {
                // TODO send e-mail to user
            }

            if ( addUsersToGroupCheckbox.isChecked() )
            {
                String groupName = groupToAddUsers.getValue();

                PermissionsManager.getInstance().addUserToGroup( username,
                    GroupFactory.groupWithName( groupName ) );
            }
        }

        uploadUsersWindow.detach();
    }


    // ----------------------------------------------------------
    private String generateRandomPassword()
    {
        int len = 10;

        StringBuilder sb = new StringBuilder( len );
        Random random = new Random();

        for ( int i = 0; i < len; i++ )
        {
            sb.append( PASSWORD_CHARS.charAt( random.nextInt( PASSWORD_CHARS.length() ) ) );
        }

        return sb.toString();
    }


    // ----------------------------------------------------------
    @SuppressWarnings("unchecked")
    private void repopulateCsvTable()
    {
        csvTable.getListhead().getChildren().clear();
        csvTable.getItems().clear();

        if ( csvData == null )
        {
            return;
        }

        int startRow = 0;
        if ( hasHeaderRowCheckbox.isChecked() )
            startRow = 1;

        for ( int row = startRow; row < csvData.length; row++ )
        {
            String[] rowData = csvData[row];

            if ( row == startRow )
            {
                // Put in the header cells the first time through.

                Listheader header = new Listheader();
                csvTable.getListhead().getChildren().add( header );

                for ( int col = 0; col < rowData.length; col++ )
                {
                    header = new Listheader();
                    csvTable.getListhead().getChildren().add( header );
                }
            }

            createColumnTypeSelectors( csvTable.getListhead() );

            Listitem item = new Listitem();
            item.setValue( rowData );

            Listcell checkCell = new Listcell();
            checkCell.setParent( item );

            Checkbox checkbox = new Checkbox();
            checkbox.setChecked( true );
            checkbox.setParent( checkCell );

            for ( int col = 0; col < rowData.length; col++ )
            {
                Listcell cell = new Listcell();
                cell.setStyle( "padding: 0px 4px; white-space: nowrap; min-width: 8em" );
                cell.setLabel( rowData[col] );
                cell.setParent( item );
            }

            csvTable.getItems().add( item );
        }
    }


    // ----------------------------------------------------------
    @SuppressWarnings("unchecked")
    private void createColumnTypeSelectors( Listhead head )
    {
        final List<Component> children = head.getChildren();

        EventListener selectorListener = new EventListener()
        {
            public void onEvent( Event event ) throws Exception
            {
                Listbox affected = (Listbox)event.getTarget();

                for ( int i = 1; i < children.size(); i++ )
                {
                    Listheader header = (Listheader)children.get( i );
                    Listbox listbox = (Listbox)header.getChildren().get( 0 );

                    if ( listbox != affected )
                    {
                        Listitem selItem = affected.getSelectedItem();
                        Listitem otherItem = listbox.getSelectedItem();

                        if ( selItem != null && otherItem != null
                            && selItem.getLabel().length() > 0
                            && selItem.getLabel().equals( otherItem.getLabel() ) )
                        {
                            listbox.setSelectedIndex( 0 );
                        }
                    }
                }
            }
        };

        for ( int i = 1; i < children.size(); i++ )
        {
            Listheader header = (Listheader)children.get( i );

            if ( header.getChildren().size() == 0 )
            {
                Listbox listbox = new Listbox();
                listbox.setMold( "select" );
                listbox.getItems().add( new Listitem( "" ) );
                listbox.getItems().add( new Listitem( "Username" ) );
                listbox.getItems().add( new Listitem( "Password" ) );
                listbox.addEventListener( "onSelect", selectorListener );
                listbox.setParent( header );

                listbox.getWidth();
            }
        }
    }

}
